package com.authine.cloudpivot.ext.service.contract.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapBuilder;
import com.alibaba.fastjson.JSON;
import com.authine.cloudpivot.ext.constants.ModelConst;
import com.authine.cloudpivot.ext.dto.model.contract.ContractServiceFeeFormula;
import com.authine.cloudpivot.ext.dto.model.contract.ContractServiceFeeTemplate;
import com.authine.cloudpivot.ext.service.contract.ContractServiceFeeTemplateService;
import com.authine.cloudpivot.ext.utils.BigDecimalUtils;
import com.authine.cloudpivot.ext.utils.BoServiceUtils;
import com.authine.cloudpivot.ext.utils.JdbcTemplateUtils;
import com.authine.common.util.ObjectMapperUtils;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;

public class ContractServiceFeeTemplateServiceImpl implements ContractServiceFeeTemplateService {
    @Override
    public void update(ContractServiceFeeTemplate template) {
        ContractServiceFeeTemplate serviceFeeTemplate = JdbcTemplateUtils.builder(ModelConst.SERVICE_FEE_TEMPLATE).selectField("*")
                .eq("id", template.getId())
                .queryForPOJO(ContractServiceFeeTemplate.class);
        Assert.notNull(serviceFeeTemplate, "要修改的代购服务费模板不存在");
        this.verify(template);
        BoServiceUtils.updateMainBo(ModelConst.SERVICE_FEE_TEMPLATE, BeanUtil.beanToMap(template));
        List<Map<String, Object>> recordMapList = new ArrayList<>();
        Map<String, Object> basicMap = new HashMap<>();
        basicMap.put("template_id", template.getId());
        basicMap.put("organization_id", template.getOrganization_id());
        basicMap.put("organization_name", template.getOrganization_id().getName());
        boolean organizationId = Objects.equals(template.getOrganization_id(), serviceFeeTemplate.getOrganization_id());
        if (template.getOrganization_id() != null && !organizationId) {
            Map<String, Object> param = new HashMap<>();
            param.putAll(basicMap);
            param.put("column_name", "organization_id");
            param.put("old_value", serviceFeeTemplate.getOrganization_id().getId());
            param.put("new_value", template.getOrganization_id().getId());
            recordMapList.add(param);
        }

        if (!organizationId) {
            Map<String, Object> param = new HashMap<>();
            param.putAll(basicMap);
            param.put("column_name", "organization_name");
            param.put("old_value", serviceFeeTemplate.getOrganization_id().getName());
            param.put("new_value", template.getOrganization_id().getName());
            recordMapList.add(param);
        }

        boolean startDay = template.getStart_day() != null && template.getStart_day().intValue() == serviceFeeTemplate.getStart_day().intValue();
        if (template.getStart_day() != null && !startDay) {
            Map<String, Object> param = new HashMap<>();
            param.putAll(basicMap);
            param.put("column_name", "start_day");
            param.put("old_value", serviceFeeTemplate.getStart_day());
            param.put("new_value", template.getStart_day());
            recordMapList.add(param);
        }

        boolean incrementDay = template.getIncrement_day() !=null && template.getIncrement_day().compareTo(serviceFeeTemplate.getIncrement_day()) == 0;
        if (!incrementDay) {
            Map<String, Object> param = new HashMap<>();
            param.putAll(basicMap);
            param.put("column_name", "increment_day");
            param.put("old_value", serviceFeeTemplate.getIncrement_day());
            param.put("new_value", template.getIncrement_day());
            recordMapList.add(param);
        }

        boolean bufferDay = template.getBuffer_day() != null && template.getBuffer_day().compareTo(serviceFeeTemplate.getBuffer_day()) == 0;
        if (!bufferDay) {
            Map<String, Object> param = new HashMap<>();
            param.putAll(basicMap);
            param.put("column_name", "buffer_day");
            param.put("old_value", serviceFeeTemplate.getBuffer_day());
            param.put("new_value", template.getBuffer_day());
            recordMapList.add(param);
        }

        boolean annualizedRatio = template.getAnnualized_ratio() !=null && template.getAnnualized_ratio().compareTo(serviceFeeTemplate.getAnnualized_ratio()) == 0;
        if (!annualizedRatio) {
            Map<String, Object> param = new HashMap<>();
            param.putAll(basicMap);
            param.put("column_name", "annualized_ratio");
            param.put("old_value", serviceFeeTemplate.getAnnualized_ratio());
            param.put("new_value", template.getAnnualized_ratio());
            recordMapList.add(param);
        }

        boolean otherRatio = template.getOther_ratio() != null && template.getOther_ratio().compareTo(serviceFeeTemplate.getOther_ratio()) == 0;
        if (!otherRatio) {
            Map<String, Object> param = new HashMap<>();
            param.putAll(basicMap);
            param.put("column_name", "other_ratio");
            param.put("old_value", serviceFeeTemplate.getOther_ratio());
            param.put("new_value", template.getOther_ratio());
            recordMapList.add(param);
        }


        boolean formula = Objects.equals(template.getFormula(), serviceFeeTemplate.getFormula());
        if (template.getFormula() != null && !formula) {
            Map<String, Object> param = new HashMap<>();
            param.putAll(basicMap);
            param.put("column_name", "formula");
            param.put("old_value", serviceFeeTemplate.getOther_ratio());
            param.put("new_value", template.getOther_ratio());
            recordMapList.add(param);
        }
        if (recordMapList.size()>0){
            BoServiceUtils.createBo(ModelConst.SERVICE_FEE_TEMPLATE_RECORD, recordMapList);
        }

    }

    @Override
    public BigDecimal getContractServiceFee(String organizationId, BigDecimal amount, int days) {
        if (days < 0) {
            return BigDecimal.ZERO;
        }
        //根据组织ID和天数获取代购服务费模板
        String sql = "select * from " + JdbcTemplateUtils.getTableName(ModelConst.SERVICE_FEE_TEMPLATE) +
                " where JSON_UNQUOTE(organization_id -> '$.id') = :organizationId" +
                " and start_day <= :day and (increment_day is null or (start_day + increment_day) >= :day)";
        Map<String, Object> param = MapBuilder.create(new HashMap<String, Object>())
                .put("organizationId", organizationId)
                .put("day", days).build();
        List<ContractServiceFeeTemplate> templateList = JdbcTemplateUtils.queryForPOJOList(sql, param, ModelConst.SERVICE_FEE_TEMPLATE, ContractServiceFeeTemplate.class);

        Assert.isTrue(null != templateList && templateList.size() == 1, "账期相差" + days + "天，未获取到对应的服务费模板，请检查是否设置服务费模板");

        //获取模板中的JSON字符串公式
        ContractServiceFeeTemplate template = templateList.get(0);
        List<ContractServiceFeeFormula> formulaList;
        try {
            formulaList = JSON.parseArray(template.getFormula(), ContractServiceFeeFormula.class);
        } catch (Exception e) {
            throw new RuntimeException("公式格式有误");
        }

        //把参数赋值到model中，目的是为了更好的从公式枚举中获取对应的get值
        template.setAmount(amount);
        template.setDays(new BigDecimal(days));

        BigDecimal result = BigDecimal.ZERO;
        for (int i = 0; i < formulaList.size() - 1; i++) {
            ContractServiceFeeFormula formula = formulaList.get(i);

            //如果下标为1则从model中获取第一数值，以后都从结果中进行计算
            BigDecimal variable;
            if (i == 0) {
                variable = formula.getVariable().variable.apply(template);
            } else {
                variable = result;
            }

            BigDecimal variableNext = formulaList.get(i + 1).getVariable().variable.apply(template);
            result = formula.getOperator().func.apply(variable, variableNext);
        }

        return result;
    }

    /**
     * 验证其实天数+增量天数不能存在交集.
     * 验证公式格式是否正确
     *
     * @param entity 代购服务费模板
     */
    private void verify(ContractServiceFeeTemplate entity) {
        this.verifyFormula(entity.getFormula());
        List<ContractServiceFeeTemplate> templateList = JdbcTemplateUtils.builder(ModelConst.SERVICE_FEE_TEMPLATE).selectField("*")
                .eq("organization_id", JSON.toJSONString(entity.getOrganization_id()))
                .queryForPOJOList(ContractServiceFeeTemplate.class);
        if (CollectionUtils.isEmpty(templateList)) {
            return;
        }

        if (templateList.size() == 1 && entity.getId().equals(templateList.get(0).getId())) {
            return;
        }

        for (ContractServiceFeeTemplate template : templateList) {
            if (entity.getId().equals(template.getId())) {
                continue;
            }

            Assert.isTrue(this.verifyIntersection(entity, template), "起始天数+增量天数不可存在交集");
        }
    }

    private boolean verifyIntersection(ContractServiceFeeTemplate source, ContractServiceFeeTemplate target) {

        if (null == target.getIncrement_day() && null == source.getIncrement_day()) {
            return false;
        }

        if (null == target.getIncrement_day()) {
            if (BigDecimalUtils.add(source.getStart_day(), source.getIncrement_day()).compareTo(target.getStart_day()) >= 0) {
                return false;
            } else {
                return true;
            }
        }

        if (null == source.getIncrement_day()) {
            if (BigDecimalUtils.add(target.getStart_day(), target.getIncrement_day()).compareTo(source.getStart_day()) >= 0) {
                return false;
            } else {
                return true;
            }
        }

        for (int i = target.getStart_day().intValue(); i < target.getStart_day().intValue() + target.getIncrement_day().intValue(); i++) {

            for (int j = source.getStart_day().intValue(); j < source.getStart_day().intValue() + source.getIncrement_day().intValue(); j++) {
                if (i == j) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * 验证公式
     * @param formulaStr
     */
    private void verifyFormula(String formulaStr) {
        List<ContractServiceFeeFormula> formulaList = ObjectMapperUtils.readValue(formulaStr, ContractServiceFeeFormula.class);


        Assert.isTrue(formulaList.size() > 1, "公式格式有误");
        for (int i = 0; i < formulaList.size(); i++) {
            ContractServiceFeeFormula formula = formulaList.get(i);
            Assert.notNull(formula.getVariable(), "公式变量不能为空");
            if (i == 0 || i != formulaList.size() - 1) {
                Assert.notNull(formula.getOperator(), "公式运算符不能为空");
            }
        }
    }
}
